package com.ee.bruscar.generator;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.ee.bruscar.configuration.InterfaceConfiguration;
import com.ee.bruscar.datamaker.DataMaker;

public class ImplementorFactory {
	
	private InterfaceConfiguration config;
	
	private StringBuilder importBuilder;
	
	public ImplementorFactory(InterfaceConfiguration config)
	{
		this.config = config;
	}

	public void generateImplementorSource( Class interfaceToImplement )
	{
		StringBuilder start = new StringBuilder();
		importBuilder = new StringBuilder();
		StringBuilder builder = new StringBuilder();
		
		Method[] methods = interfaceToImplement.getMethods();
		
		String packageName = getPackage( config.getStubClass() );
		start.append("package " + packageName + "\n");
		
		String className = getSimpleClassName( config.getStubClass() );
		builder.append("class " + className + " implements "+ interfaceToImplement.getName() + "\n");
		builder.append("{\n");
		
		for( int i = 0 ; i< methods.length ; i++ )
		{
			Method method = methods[i];
			createSignature(builder, method);
			createMethodBody( builder, method );
			}
		builder.append("}\n");
		
		URL url = interfaceToImplement.getResource(".");
		String generatedFileName = convertClassToPath( config.getStubClass() );
		String completeFile = config.getSrcPath() + "/" + generatedFileName;
		
		try 
		{
			BufferedWriter out = new BufferedWriter(new FileWriter(completeFile));
		    out.write( start.toString() );
		    out.write( importBuilder.toString() );
		    out.write( builder.toString() );
		    out.close();
		}
		catch (IOException e)
		{
		}

		
		builder.toString();
		
	}
	private String convertClassToPath( String className )
	{
		return className.replace(".", "/" ) + ".java";
	}
	
	private String getSimpleClassName( String className )
	{
		int index = className.lastIndexOf(".");
		return className.substring(index + 1, className.length() );
	}
	
	private String getPackage( String className )
	{
		int index = className.lastIndexOf(".");
		return className.substring(0, index) + ";";
		
	}
	
	private void createMethodBody( StringBuilder builder, Method method ) 
	{
		
		builder.append("{\n");
		String returnObjectName = getVarName(method.getReturnType());

		inspect( method, builder, returnObjectName );
		builder.append("return " + returnObjectName + ";\n");
		builder.append( "}\n" );
	}
	
	private void inspect( Method method, StringBuilder builder, String variableName )
	{
		
		if( method.getReturnType().getName().equals("java.util.List" ) )
		{
			Type returnType = method.getGenericReturnType();
			if(returnType instanceof ParameterizedType)
			{
			    ParameterizedType type = (ParameterizedType) returnType;
			    Type[] typeArguments = type.getActualTypeArguments();
			    Class typeArgClass = (Class) typeArguments[0];
		        System.out.println("typeArgClass = " + typeArgClass);
		        builder.append("//its a list\n");
		        addImport(ArrayList.class);
		        builder.append("ArrayList<"+ typeArgClass.getName() +"> " + variableName + " = new ArrayList<"+ typeArgClass.getName()  +">();\n");
		        for( int i = 0 ; i < 2 ; i++ )
		        {
		        	String varName = getVarName( typeArgClass );
		        	createStubCode( typeArgClass, builder, varName );
		        	builder.append( variableName + ".add("+  varName +");\n");
		        }
			}

			
			return;
		}
		else
		{
			Class returnType = method.getReturnType();
			createStubCode(method.getReturnType(), builder, variableName);
		}
	
	}

	private void createStubCode( Class returnType, StringBuilder builder, String variableName )
	{
		builder.append("\n");
		builder.append("//new declaration\n");
		builder.append( returnType.getName() + " "+variableName + " = new " + returnType.getName() + "();\n" );
		List<Method> setters = getSetters( returnType.getMethods() );
		
		for( Method setter : setters )
		{
			Class setterArg = setter.getParameterTypes()[0];
			if( isPrimitive(setterArg) )
			{
				if( setterArg == String.class )
				{
					String value = getDummyDataFor( setter.getName(), returnType.getName() );
					builder.append( variableName + "." + setter.getName() + "(\"" +  value  + "\");\n");
				}
				else if( setterArg == Date.class )
				{
					addImport( Date.class );
					builder.append( variableName + "." + setter.getName() + "( new Date() );\n");
				}
				else if( setterArg.getName().equals("double"))
				{
					double random = Math.random();
					builder.append( variableName + "." + setter.getName() + "(" + new Double(random).toString() + ");\n");
				}
				
			}
			else
			{
				String objectName = getVarName(setterArg);
				createStubCode(setterArg, builder, objectName );
				builder.append("//setting on parent\n" );
				builder.append( variableName + "." + setter.getName() + "(" +  objectName + ");\n" );
				builder.append("  ");
			}
		}
	}
	
	private void addImport(Class clazz)
	{
		if( importBuilder.indexOf("import " + clazz.getName() )  == -1 )
		{
			importBuilder.append("import " + clazz.getName() + ";\n" );
		}
	}

	private String getDummyDataFor(String setterName, String className )
	{
		String memberName = convertSetterToMemberName( setterName );
		String hint = config.getHint( className, memberName );
		
		if( hint != memberName )
		{
			DataMaker maker = new DataMaker();
			return maker.createData( hint );
		}
		
		return memberName;
			
	}

	private String convertSetterToMemberName(String setterName)
	{
		assert setterName.startsWith("set");
		String stripped = setterName.substring(3);
		return lowercaseFirstLetter(stripped);
	}

	private String getVarName( Class clazz )
	{
		double random = Math.random();
		String randomString = new Double( random ).toString();
		randomString = randomString.replace("0.", "");
		randomString = randomString.substring(0, 4);
		
		return lowercaseFirstLetter( clazz.getSimpleName() ) + randomString;	
	}
	
	private String lowercaseFirstLetter( String value )
	{
		String firstLetter = value.substring(0, 1);
		firstLetter = firstLetter.toLowerCase();
		return firstLetter + value.substring(1, value.length() );
		
	}
	private List<Method> getSetters(Method[] methods) 
	{
		List<Method> setters = new ArrayList<Method>();
		
		for( int i = 0 ; i < methods.length ; i++ )
		{
			Method method = methods[i];
			if( method.getName().startsWith("set"))
			{
				setters.add( method );
			}
		}
		
		return setters;
	}

	private void createSignature( StringBuilder builder, Method method )
	{
		builder.append("public "); 
		builder.append( method.getReturnType().getName() + " " + method.getName() );
		builder.append( "( " );
		builder.append( getParamaters( method.getParameterTypes() ) );
		builder.append( " )" );
	}

	private String getParamaters(Class[] parameterTypes) 
	{
		String result = "";
		for( int i = 0; i < parameterTypes.length ; i++ )
		{
			String comma = ", ";
			if ( i == parameterTypes.length - 1 )
			{
				comma = "";
			}
			Class clazz = parameterTypes[i];
			result += clazz.getName() + " arg" + i + comma;
		}
		return result;
	}
	
	@SuppressWarnings("unchecked")
    public boolean isPrimitive( Class type )
    {
        if( type.getName().equals("boolean"))
        {
            return true;
        }
        if( type.getName().equals("int"))
        {
            return true;
        }
        if( type.getName().equals("long"))
        {
            return true;
        }
        if( type.getName().equals("double"))
        {
            return true;
        }
        if( type == java.lang.annotation.Annotation.class )
        {
            return true;
        }
        if( type == String.class )
        {
            return true;
        }
        if( type == Integer.class )
        {
            return true;
        }
        if( type == Boolean.class )
        {
            return true;
        }
        if( type == Date.class )
        {
            return true;
        }
        return false;
    }

}
